﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace UI.Dendrogram
{
    public class SplitDiagramUtil
    {
        public static Split DisplaySplits(Canvas canvas, Orientation orientation,
            HierarchicalData root)
        {
            double totalDistanceFromHeadToFoot = GetMaxDistance(root);
            int branchCount = GetBranchCount(root);
            double width = 400;
            double height = 200;
            if(orientation==Orientation.Horizontal)
            {
                width =200;
                height = 400;
            }
            double distanceMeasureScale = GetDistanceMeasureScale(height, totalDistanceFromHeadToFoot);
            double branchSpace = GetBranchSpace(width, branchCount);
            Point rootPos=new Point(400/2, 5);
            if(orientation==Orientation.Horizontal)
            {
                rootPos=new Point(5, 400/2);
            }
            List<Split> allSplits=new List<Split>();
            Split rootSplit = AddSplit(canvas,orientation,
                new Rect(0, 0, width, height),
                root, rootPos,
                distanceMeasureScale, branchSpace, ref allSplits);
            UpdateSplitLayout(allSplits, rootSplit, new Rect(0,0,width,height), 
                rootPos, branchSpace, distanceMeasureScale);
            return rootSplit;
        }

        private static double GetBranchSpace(double width, int branchCount)
        {
            width -= 20*2;
            return width/branchCount;
        }

        private static double GetDistanceMeasureScale(double height, double distanceFromHeadToFoot)
        {
            return (height*2/3)/distanceFromHeadToFoot;
        }

        private static int GetBranchCount(HierarchicalData root)
        {
            if (!IsHierarchicalDataClusterType(root))
                return 1;
            int branchCount = 2;
            if(root.LeftChild!=null)
            {
                int leftBranchCount = GetBranchCount(root.LeftChild);
                branchCount += (leftBranchCount - 1);
            }
            if(root.RightChild!=null)
            {
                int rightBranchCount = GetBranchCount(root.RightChild);
                branchCount += (rightBranchCount - 1);
            }
            return branchCount;
        }

        private static bool IsHierarchicalDataClusterType(HierarchicalData data)
        {
            if(data==null)
                return false;
            if(data.LeftChild==null && data.RightChild==null)
                return false;
            return true;
        }

        private static double GetMaxDistance(HierarchicalData root)
        {
            double maxDistance = root.Distance;
            double leftChildDistance = 0.1;
            if (root.LeftChild != null)
                leftChildDistance = GetMaxDistance(root.LeftChild);
            double rightChildDistance = 0.1;
            if (root.RightChild != null)
                rightChildDistance = GetMaxDistance(root.RightChild);
            return maxDistance + Math.Max(leftChildDistance, rightChildDistance);
        }

        private static Split AddSplit(Canvas canvas,
            Orientation orientation, Rect bounds,
            HierarchicalData root, Point startPos,
            double distanceScale, double branchSpace,
            ref List<Split> allSplits)
        {
            Split split=new Split(
                canvas, bounds, orientation, startPos,
                GetBranchCount(root.LeftChild),
                GetBranchCount(root.RightChild),
                Math.Max(0.05,root.Distance),
                root.LeftChild,
                ((root.LeftChild==null)? string.Empty: root.LeftChild.Label),
                root.RightChild,
                ((root.RightChild==null)?string.Empty:root.RightChild.Label),
                IsHierarchicalDataClusterType(root.LeftChild),
                IsHierarchicalDataClusterType(root.RightChild),
                root, branchSpace, distanceScale);
            canvas.Children.Add(split);
            if(allSplits!=null)
                allSplits.Add(split);
            if (root.LeftChild != null)
            {
                AddSplit(
                    canvas, orientation, bounds,
                    root.LeftChild, split.LeftBranch.ToPos,
                    distanceScale, branchSpace, ref allSplits);
            }
            if(root.RightChild!=null)
            {
                AddSplit(
                    canvas, orientation, bounds,
                    root.RightChild, split.RightBranch.ToPos,
                    distanceScale, branchSpace, ref allSplits);
            }
            return split;
        }

        private static void UpdateSplitLayout(
            List<Split> allSplits, Split split, 
            Rect bounds, Point startPos, 
            double branchSpace, double distanceScale)
        {
            split.UpdateLayout(bounds,startPos,branchSpace,distanceScale);
            if(split.LeftBranch.Data!=null)
            {
                foreach(Split leftSplit in allSplits)
                {
                    if(leftSplit.Data==split.LeftBranch.Data)
                    {
                        UpdateSplitLayout(allSplits, leftSplit,
                            bounds,split.LeftBranch.ToPos,
                            branchSpace,distanceScale);
                        break;
                    }
                }
            }
            if(split.RightBranch.Data!=null)
            {
                foreach(Split rightSplit in allSplits)
                {
                    if(rightSplit.Data==split.RightBranch.Data)
                    {
                        UpdateSplitLayout(allSplits,rightSplit,
                            bounds,split.RightBranch.ToPos,
                            branchSpace,distanceScale);
                        break;
                    }
                }
            }
        }
    }
}
